package com.zhuojy.equity.mall.admin.common.utils;

import cn.hutool.core.util.IdUtil;
import com.zhuojy.equity.mall.admin.common.support.Result;
import org.slf4j.Logger;
import org.slf4j.MDC;

/**
 * 统一日志打印工具类
 */
public class LogUtil {

	/**
	 * 日志跟踪：TRACE_LOG_ID
	 */
	private static final String TRACE_LOG_ID = "TRACE_LOG_ID";

	/**
	 * 状态：成功
	 */
	private static final String SUCCESS = "Success";

	/**
	 * 状态：失败
	 */
	private static final String FAILURE = "Failure";

	/**
	 * 成功默认响应码：0000
	 */
	private static final String SUCCESS_DEFAULT_CODE = "0000";

	/**
	 * 时间单位毫秒：ms
	 */
	private static final String TIME_UNIT_MS = "ms";

	/**
	 * 请求关键字
	 */
	private static final String START_CALL = "call ";

	/**
	 * 中括号开始[
	 */
	private static final String START_PARENTHESES = "[";

	/**
	 * 中括号结束]
	 */
	private static final String END_PARENTHESES = "]";

	/**
	 * 请求参数前缀
	 */
	private static final String START_PARAMETER = " PARAMETER:[{}]";

	/**
	 * 含有集合大小的请求参数前缀
	 */
	private static final String START_PARAMETER_WITH_SIZE = " PARAMETER:[请求数据集合大小:{}]";

	/**
	 * 结果日志打印前缀
	 */
	private static final String END_PREFIX = " RESPONSE:Result[{}]";

	/**
	 * 含有集合大小的结果日志打印前缀
	 */
	private static final String END_PREFIX_WITH_SIZE = " RESPONSE:Result[返回数据集合大小:{}]";

	/**
	 * 打印入参日志
	 *
	 * @param log        日志实现类
	 * @param methodName 方法名
	 * @param reqDTO     请求参数
	 */
	public static void printStartLogInfo(Logger log, String methodName, Object reqDTO) {
		MDC.put(TRACE_LOG_ID, IdUtil.fastSimpleUUID());
		log.info(appendStartPrefix(getSimpleClazzName(log.getName()), methodName), reqDTO);
	}

	/**
	 * 打印含有集合大小的入参日志
	 *
	 * @param log        日志实现类
	 * @param methodName 方法名枚举
	 * @param size       集合大小
	 */
	public static void printStartLogInfoWithSize(Logger log, String methodName, int size) {
		log.info(appendStartPrefixWithSize(getSimpleClazzName(log.getName()), methodName), size);
	}

	/**
	 * 打印返回日志
	 *
	 * @param log        日志实现类
	 * @param methodName 方法名枚举
	 * @param startTime  开始执行时间
	 * @param result     结果
	 */
	public static void printEndLogInfo(Logger log, String methodName, Long startTime, Result<?> result) {
		if (result.isSuccess()) {
			log.info(appendEndPrefix(getSimpleClazzName(log.getName()), methodName, startTime, result), result);
			return;
		}
		log.warn(appendEndPrefix(getSimpleClazzName(log.getName()), methodName, startTime, result), result);
	}

	/**
	 * 打印含有集合大小的返回日志
	 *
	 * @param log        日志实现类
	 * @param methodName 方法名枚举
	 * @param startTime  开始执行时间
	 * @param size       集合大小
	 */
	public static void printEndLogInfoWithSize(Logger log, String methodName, Long startTime, int size) {
		log.info(appendEndPrefixWithSize(getSimpleClazzName(log.getName()), methodName, startTime), size);
	}

	/**
	 * 获取接口调用状态
	 *
	 * @param result 结果对象
	 *
	 * @return 接口调用状态
	 */
	private static String getStatus(Result<?> result) {
		return result.isSuccess() ? SUCCESS : FAILURE;
	}

	/**
	 * 获取接口响应码
	 *
	 * @param result 结果对象
	 *
	 * @return 接口调用响应码
	 */
	private static String getResultCode(Result<?> result) {
		return result.isSuccess() ? SUCCESS_DEFAULT_CODE : result.getErrCd();
	}

	/**
	 * 获取类名
	 *
	 * @param clazzName 类
	 *
	 * @return 类名
	 */
	private static String getSimpleClazzName(String clazzName) {
		return clazzName.substring(clazzName.lastIndexOf('.') + 1);
	}

	/**
	 * 获取接口调用时间，毫秒
	 *
	 * @param startTime 开始调用时间
	 *
	 * @return 接口调用时间
	 */
	private static Long getTime(Long startTime) {
		return System.currentTimeMillis() - startTime;
	}

	/**
	 * 拼接请求日志前缀字符串
	 *
	 * @param clazzName  类名
	 * @param methodName 方法名
	 *
	 * @return 拼接请求日志前缀字符串
	 */
	private static String appendStartPrefix(String clazzName, String methodName) {
		// 拼接前缀
		StringBuilder prefix = appendPrefix(clazzName, methodName);
		prefix.append(START_PARAMETER);
		return prefix.toString();
	}

	/**
	 * 拼接含有集合大小的请求日志前缀字符串
	 *
	 * @param clazzName  类名
	 * @param methodName 方法名
	 *
	 * @return 拼接含有集合大小的请求日志前缀字符串
	 */
	private static String appendStartPrefixWithSize(String clazzName, String methodName) {
		// 拼接前缀
		StringBuilder prefix = appendPrefix(clazzName, methodName);
		prefix.append(START_PARAMETER_WITH_SIZE);
		return prefix.toString();
	}

	/**
	 * 拼接含有方法描述的返回日志前缀字符串
	 *
	 * @param clazzName  类名
	 * @param methodName 方法名
	 * @param startTime  开始执行时间
	 * @param result     响应结果
	 *
	 * @return 拼接返回日志前缀字符串
	 */
	private static String appendEndPrefix(String clazzName, String methodName, Long startTime, Result<?> result) {
		StringBuilder prefix = appendPrefix(clazzName, methodName);
		prefix.append(START_PARENTHESES);
		// 响应时间
		prefix.append(getTime(startTime));
		// 响应时间单位
		prefix.append(TIME_UNIT_MS);
		prefix.append(END_PARENTHESES);
		prefix.append(START_PARENTHESES);
		// 调用结果
		prefix.append(getStatus(result));
		prefix.append(END_PARENTHESES);
		prefix.append(START_PARENTHESES);
		// 返回码
		prefix.append(getResultCode(result));
		prefix.append(END_PARENTHESES);
		prefix.append(END_PREFIX);
		return prefix.toString();
	}

	/**
	 * 拼接含有集合大小的返回日志前缀字符串
	 *
	 * @param clazzName  类名
	 * @param methodName 方法名
	 * @param startTime  开始执行时间
	 *
	 * @return 拼接含有集合大小的返回日志前缀字符串
	 */
	private static String appendEndPrefixWithSize(String clazzName, String methodName, Long startTime) {
		StringBuilder prefix = appendPrefix(clazzName, methodName);
		prefix.append(START_PARENTHESES);
		// 响应时间
		prefix.append(getTime(startTime));
		// 响应时间单位
		prefix.append(TIME_UNIT_MS);
		prefix.append(END_PARENTHESES);
		prefix.append(START_PARENTHESES);
		// 调用结果
		prefix.append(SUCCESS);
		prefix.append(END_PARENTHESES);
		prefix.append(START_PARENTHESES);
		// 返回码
		prefix.append(SUCCESS_DEFAULT_CODE);
		prefix.append(END_PARENTHESES);
		prefix.append(END_PREFIX_WITH_SIZE);
		return prefix.toString();
	}

	/**
	 * 拼接日志公共的前缀
	 *
	 * @param clazzName  类名
	 * @param methodName 方法名
	 *
	 * @return 公共的前缀
	 */
	private static StringBuilder appendPrefix(String clazzName, String methodName) {
		StringBuilder prefix = new StringBuilder();
		prefix.append(START_CALL);
		prefix.append(START_PARENTHESES);
		// 类名
		prefix.append(clazzName);
		prefix.append(END_PARENTHESES);
		prefix.append(START_PARENTHESES);
		// 方法名
		prefix.append(methodName);
		prefix.append(END_PARENTHESES);
		return prefix;
	}
}
